#include<stdio.h>
#include<math.h>
#include<time.h>
#include<string.h>
#include<stdlib.h>

void Feature_Train(double *,double *);
void Feature_Initialize_Weight_Matrices(double);


unsigned long iv,hv,ov,choice,no_of_dataset,
			     iteration_counter=0,Snap_Frequency;

void Gather_Information_Of_Recordset();



double *in_vect,
             *out_vect,
             *calculated_out_vect,
             *hidden_vect,
	     *del_output,
	     *del_hidden,
	     *gamma_n,*gamma_prev,
	     **change_hidden_output,
	     **input_hidden,
	     **hidden_output,
	     **change_input_hidden;

double alpha=(double)0.8,
       beta=(double)0.2,
       mu=(double)0.8,
       gamma_val,
       previous_error=(double)0.0,
       recent_error=(double)0.0,
       test_error=(double)0.0,
       previous_test_error=(double)0.0;
     
unsigned long l=0,modify=0;

char data_source_path[100],
     *weight_matrix_path="weight.txt",
     gamma_path[100];


FILE *fp_source,*fp_weight,*fp_result,*fpgamma,*fp_master;



void store_file(unsigned long);
void store_result(unsigned long);




/**********************************************main section****************************************************/
void main(void){

	
        unsigned long count,itr;
        double tot_err,avg_err;
        Snap_Frequency=100;
        printf("\n Program for Feature Selection ");
        
        printf("\n Enter the Source File Path::");
        scanf("%s",data_source_path); 
	  
        printf("\n Enter the gamma path::");
        scanf("%s",gamma_path); 
        
        printf("\n Enter the number of Inputs::");
        scanf("%lu",&iv); 
	
	  printf("\n Enter the number of Outputs::");
        scanf("%lu",&ov); 
        
        printf("\n Enter the number of Hidden Nodes::");
        scanf("%lu",&hv); 
        
        printf("\n Enter the number of Iteration::");
        scanf("%lu",&itr); 
        
        printf("\n Enter the Snap Frequency(Interval)");
        scanf("%lu",Snap_Frequency);

	
	 Feature_Initialize_Weight_Matrices(-5.0);/*enter the initial value of gama & choice*/

	
       for(count=0;count<itr;count++){
		Feature_Train(&tot_err,&avg_err);
		printf("%09.3lf %09.3lf \nITR :: %lu",tot_err,avg_err,count);
	}
	printf("\nTOT ERR :%09.3lf AVG ERR:%09.3lf \nITR :: %lu",tot_err,avg_err,count);
}

/****************************************************************************************************************/


void modify_alpha_beta(void)
{
	alpha=(double)0.9*alpha;
	beta=(double)0.99*beta;
	if(alpha<0.1||beta<0.1){
		alpha=(double)0.9;
		beta=(double)0.2;
	}
	
	return;
}

void matrix_reloaded(void)
{
	unsigned long i=0,j=0;	
	double a,temp,t1,t2;

	fp_weight=fopen(weight_matrix_path,"r");
	fscanf(fp_weight,"%lu %lf %lf",&i,&t1,&t2);

	for(i=0;i<hv;i++){
		for(j=0;j<ov;j++){
			fscanf(fp_weight,"%lf",&temp);
			hidden_output[i][j]=temp;
		}
	}
	for(i=0;i<iv;i++){
		for(j=0;j<hv;j++){
			fscanf(fp_weight,"%lf",&temp);
			input_hidden[i][j]=temp;
		}
	}
	for(i=0;i<hv;i++){
		for(j=0;j<ov;j++){
			fscanf(fp_weight,"%lf",&a);
			change_hidden_output[i][j]=a;
		}
	}
	for(i=0;i<iv;i++){
		for(j=0;j<hv;j++){
			fscanf(fp_weight,"%lf",&a);
			change_input_hidden[i][j]=a;
		}
	}
	fclose(fp_weight);
	return;
}

double activation(double x)
{
	double a,b,c;
	a=(double)exp((double)(-x));
	b=a+(double)1.0;
	c=(double)1.0/b;
	return(c);
}

double F(double x){
	double a,b,c;
	a=(double)exp((double)(x));
	b=a+(double)1.0;
	b=b*b;
	c=a/b;
	return(c);
}


void calculate_total_error()
{
	unsigned long r,k=0;
	double x=0,t1=0,a,temp;

/****************************************************************************
					CALCULATION OF HIDDEN VECTOR
/****************************************************************************/
	for(k=0;k<hv;k++){
		t1=0;
		x=0;
		for(r=0;r<iv;r++){
			t1=input_hidden[r][k]*in_vect[r];
			x=x+t1;
		}
		hidden_vect[k]=activation(x);
	}
/***************************************************************************
					CALCULATION OF OUTPUT VECTOR
***************************************************************************/
	for(k=0;k<ov;k++){
		x=0;
		t1=0;
		for(r=0;r<hv;r++){
			t1=hidden_output[r][k]*hidden_vect[r];
			x=x+t1;
		}
		calculated_out_vect[k]=activation(x);
		temp=(calculated_out_vect[k] - out_vect[k]);
		a=(temp)*(temp);
		recent_error=recent_error + a;
	}
	return;
}

void training(void)
{
	unsigned long r,c,k=0;
	double x=0,t1=0,t2=0,t3=0,t4=0,gamma_old;
/**************************************************************************
					CALCULATION OF HIDDEN VECTOR
**************************************************************************/
	for(k=0;k<hv;k++){
		t1=0;
		x=0;
		for(r=0;r<iv;r++){
			t1=input_hidden[r][k]*in_vect[r];
			x=x+t1;
		}
		hidden_vect[k]=activation(x);
	}
/**************************************************************************
					CALCULATION OF OUTPUT VECTOR
**************************************************************************/
	for(k=0;k<ov;k++){
		x=0;
		t1=0;
		for(r=0;r<hv;r++){
			t1=hidden_output[r][k]*hidden_vect[r];
			x=x+t1;
		}
		calculated_out_vect[k]=activation(x);
	}
/***************************************************************/

/***************************************************************
			OUTPUT LAYER ERROR CALCULATION
/***************************************************************/
	for(k=0;k<ov;k++){
		t1=(out_vect[k]-calculated_out_vect[k]);
		t2=1-calculated_out_vect[k];
		t3=t1*t2*calculated_out_vect[k];
		del_output[k]=t3;
	}
/***************************************************************/

/***************************************************************
				HIDDEN LAYER ERROR CALCULATION
/***************************************************************/
	for(r=0;r<hv;r++){
		t1=(1-hidden_vect[r])*hidden_vect[r];
		t2=0;
		t3=0;
		for(c=0;c<ov;c++){
			t2=del_output[c]*hidden_output[r][c];
			t3=t2+t3;
		}
		t4=t1*t3;
		del_hidden[r]=t4;
	}
/***************************************************************

/***************************************************************
			UPDATION OF HIDDEN-OUTPUT WEIGHT MATRIX
/***************************************************************/

	for(c=0;c<ov;c++){
		for(r=0;r<hv;r++){
			t1=hidden_vect[r]*del_output[c];
			t2=alpha*t1;
			t3=beta*change_hidden_output[r][c];
			change_hidden_output[r][c]=t2+t3;
			hidden_output[r][c]=hidden_output[r][c]+change_hidden_output[r][c];
		}
	}
/***************************************************************/

/***************************************************************
			UPDATION OF INTPUT_HIDDEN WEIGHT MATRIX
/***************************************************************/
	for(c=0;c<hv;c++){
		for(r=0;r<iv;r++){
			t1=in_vect[r]*del_hidden[c];
			t2=alpha*t1;
			t3=beta*change_input_hidden[r][c];
			change_input_hidden[r][c]=t2+t3;
			input_hidden[r][c]=input_hidden[r][c]+change_input_hidden[r][c];
		}
	}
/***************************************************************/

	for(r=0;r<iv;r++){

		gamma_old=gamma_n[r];
		x=0;
		t1=0;
		for(c=0;c<hv;c++){
			t1=del_hidden[c]*input_hidden[r][c];
			x=x+t1;
		}
		t2=mu*(in_vect[r]/activation(gamma_n[r]))*x;
		t3=t2*F(gamma_old);
		gamma_n[r]=gamma_old+t3;
	}
	return;
}

double newRand(double a)
{
	return(a*(double)rand()/32767);
}

void Gather_Information_Of_Recordset(){
        unsigned long dummy,i;

	fp_source=fopen(data_source_path,"r");
	no_of_dataset=0;	/*Record Set Counter*/

	while(!feof(fp_source))if(fgetc(fp_source) == '\n')no_of_dataset++;
	printf("\n no of dataset %lu",no_of_dataset);
	
	fclose(fp_source);

}


void Feature_Initialize_Weight_Matrices(double GAMMA_VALUE)
{
	unsigned long i,j,flag;
	double a,temp,p1,val;
         time_t t;
         
	
	
	Gather_Information_Of_Recordset();

	gamma_n=(double *)calloc(iv,sizeof(double));
	gamma_prev=(double *)calloc(iv,sizeof(double));

	
	fpgamma=fopen(gamma_path,"w");
	fclose(fpgamma);
	fp_weight=fopen(weight_matrix_path,"w");
	fprintf(fp_weight,"%lu %lf %lf",hv,alpha,beta);		
	fprintf(fp_weight,"\n");
	
	
	
	in_vect=(double *)calloc(iv,sizeof(double));
	
	hidden_vect=(double *)calloc(hv,sizeof(double));
	
	out_vect=(double *)calloc(ov,sizeof(double));
	
	calculated_out_vect=(double *)calloc(ov,sizeof(double));
	
	hidden_output=(double **)calloc(hv,sizeof(double*));
	
	del_hidden=(double *)calloc(hv,sizeof(double));
	
	del_output=(double *)calloc(ov,sizeof(double));
	
	change_hidden_output=(double **)calloc(hv,sizeof(double*));
	
	
	for(i=0;i<hv;i++){
		hidden_output[i]=(double *)calloc(ov,sizeof(double));
		change_hidden_output[i]=(double *)calloc(ov,sizeof(double));
	}
	
	input_hidden=(double **)calloc(iv,sizeof(double*));
	change_input_hidden=(double **)calloc(iv,sizeof(double*));
	
	
	for(i=0;i<iv;i++){
		input_hidden[i]=(double *)calloc(hv,sizeof(double));
		change_input_hidden[i]=(double *)calloc(hv,sizeof(double));
	}
	
	
	
	gamma_val=(double)GAMMA_VALUE;
		
	srand((unsigned)time(&t)); 
		
/***************************************************************************
				INITIALIZING HIDDEN - OUTPUT MATRIX
/***************************************************************************/
		for(i=0;i<hv;i++){
			for(j=0;j<ov;j++){
				temp=newRand(1.0)-(double)0.5;
				hidden_output[i][j]=temp;
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
/***************************************************************************
				INITIALIZING INPUT - HIDDEN MATRIX
/***************************************************************************/
		for(i=0;i<iv;i++){
			gamma_n[i]=gamma_val;
			for(j=0;j<hv;j++){
				temp=newRand(1.0)-(double)0.5;
				input_hidden[i][j]=temp;
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
/***************************************************************************
				INITIALIZING DEL - HIDDEN - OUTPUT MATRIX
/***************************************************************************/
		for(i=0;i<hv;i++){
			for(j=0;j<ov;j++){
				temp=0.0;				
				change_hidden_output[i][j]=temp;				
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
/***************************************************************************
				INITIALIZING DEL - INPUT - HIDDEN MATRIX
/***************************************************************************/
		for(i=0;i<iv;i++){
			gamma_n[i]=gamma_val;
			for(j=0;j<hv;j++){
				temp=0.0;
				change_input_hidden[i][j]=temp;;
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}

/***************************************************************************/
	
        fclose(fp_weight);


	fp_source=fopen(data_source_path,"r");

	for(l=0;l<no_of_dataset;l++){
		for(i=0;i<iv;i++){
			fscanf(fp_source,"%lf",&p1);
			in_vect[i]= p1;
			in_vect[i]=in_vect[i]*activation(gamma_n[i]);
		}
		j=i;
		for(i=0;i<ov;i++){
			fscanf(fp_source,"%lf",&p1);
			out_vect[i]= p1;
			j++;
		}
		training();
	}
	fclose(fp_source);

	fp_source=fopen(data_source_path,"r");
	recent_error=0.0;
	for(l=0;l<no_of_dataset;l++){
		for(i=0;i<iv;i++){
			fscanf(fp_source,"%lf",&p1);
			in_vect[i]= (p1-min_val[i]);
			in_vect[i] /= ( max_val[i] - min_val[i]);
			in_vect[i]=in_vect[i]*activation(gamma_n[i]);
		}
		j=i;
		for(i=0;i<ov;i++){
			fscanf(fp_source,"%lf",&p1);
			out_vect[i]= (p1-min_val[j]);
			out_vect[i] /= (max_val[j]-min_val[j]);
			j++;
		}
		calculate_total_error();
	}
	fclose(fp_source);


	previous_error=recent_error;


}


void Feature_Train(double *TotalError,double *AverageError)
{

	unsigned long i=0,j=0;
	double temp,p1;

	for(i=0;i<iv;i++){
		gamma_prev[i]=gamma_n[i];
	}

	fp_source=fopen(data_source_path,"r");

	for(l=0;l<no_of_dataset;l++){
		for(i=0;i<iv;i++){
			fscanf(fp_source,"%lf",&p1);
			in_vect[i]= (p1-min_val[i]);
			in_vect[i] /= ( max_val[i] - min_val[i]);
			in_vect[i]=in_vect[i]*activation(gamma_n[i]);
		}
		j=i;
		for(i=0;i<ov;i++){
			fscanf(fp_source,"%lf",&p1);
			out_vect[i]= (p1-min_val[j]);
			out_vect[i] /= (max_val[j]-min_val[j]);
			j++;
		}
		training();
	}
	rewind(fp_source);
	recent_error=0.0;

	for(l=0;l<no_of_dataset;l++){
		for(i=0;i<iv;i++){
			fscanf(fp_source,"%lf",&p1);
			in_vect[i]= (p1-min_val[i]);
			in_vect[i] /= ( max_val[i] - min_val[i]);
			in_vect[i]=in_vect[i]*activation(gamma_n[i]);
		}
		j=i;
		for(i=0;i<ov;i++){
			fscanf(fp_source,"%lf",&p1);
			out_vect[i]= (p1-min_val[j]);
			out_vect[i] /= (max_val[j]-min_val[j]);
			j++;
		}
		calculate_total_error();
	}
	fclose(fp_source);

	iteration_counter++;

	if((iteration_counter % Snap_Frequency)==0){
		fpgamma=fopen(gamma_path,"a");
		/*fprintf(fpgamma,"%05.0lu",iteration_counter);*/
		for(i=0;i<iv;i++){
			fprintf(fpgamma," %09.6lf",activation(gamma_n[i]));
		}
		fprintf(fpgamma,"\n");
		fclose(fpgamma);
	}

	if(previous_error < recent_error){
		modify_alpha_beta();
		matrix_reloaded();
		for(i=0;i<iv;i++){
			gamma_n[i]=gamma_prev[i];
		}
	}
	else{
		previous_error = recent_error;
		fp_weight=fopen(weight_matrix_path,"w");
		fprintf(fp_weight,"%lu %lf %lf",hv,alpha,beta);
		fprintf(fp_weight,"\n");
		for(i=0;i<hv;i++){
			for(j=0;j<ov;j++){
				temp=hidden_output[i][j];
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
		for(i=0;i<iv;i++){
			for(j=0;j<hv;j++){
				temp=input_hidden[i][j];
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
		for(i=0;i<hv;i++){
			for(j=0;j<ov;j++){
				temp=change_hidden_output[i][j];
				fprintf(fp_weight,"%lf ",temp);
			}
           		fprintf(fp_weight,"\n");
		}
		for(i=0;i<iv;i++){
			for(j=0;j<hv;j++){
				temp=change_input_hidden[i][j];
				fprintf(fp_weight,"%lf ",temp);
			}
			fprintf(fp_weight,"\n");
		}
		
		fclose(fp_weight);
	}

	*TotalError=(double)recent_error;
	*AverageError=sqrt(recent_error/no_of_dataset);


}

